package org.vacoor.nothing.web.util;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.image.RenderedImage;
import java.io.*;
import java.util.Iterator;

/**
 * 图像操作工具类
 * <p/>
 * JDK 原生 ImageIO.read/write 不支持 cmyk 模式的 jpg会丢失 ICC 信息的图片(读取后有红色蒙版)
 * http://stackoverflow.com/questions/6829428/pure-java-alternative-to-jai-imageio-for-detecting-cmyk-images
 * 解决方案: <br />
 * 1. 使用 https://github.com/haraldk/TwelveMonkeys (实现 java image spi, 操作不用任何变化)
 * 2. GraphicsMagic + im4java (需要安装 GM, 效率高)
 *
 * @author vacoor
 */
public abstract class Images {
    public static Image load(boolean waitLoaded) {
        return null;
    }

    /**
     * @param image
     * @param w
     * @param h
     * @param hints {@link Image#SCALE_FAST} {@link Image#SCALE_SMOOTH}
     * @return
     */
    public static Image scale(Image image, int w, int h, int hints) {
        // return image.getScaledInstance(w, h, Image.SCALE_FAST | Image.SCALE_SMOOTH);
        return image.getScaledInstance(w, h, hints);
    }

    public static BufferedImage drawWatermark(Image originalImg, Image watermark, int x, int y, ImageObserver observer) {
        int w = watermark.getWidth(observer);
        int h = watermark.getHeight(observer);

        if (0 > w && 0 > h) {
            waitLoaded(watermark);
            w = watermark.getWidth(observer);
            h = watermark.getHeight(observer);
        }

        return drawWatermark(originalImg, watermark, x, y, w, h, observer);
    }

    /**
     * 在图片给定区域绘制水印图片
     *
     * @param originalImg 原始图片
     * @param watermark   水印图片
     * @param x           起始 x 坐标
     * @param y           起始 y 坐标
     * @param width       水印宽度
     * @param height      水印高度
     * @param observer
     * @return
     */
    public static BufferedImage drawWatermark(Image originalImg, Image watermark, int x, int y, int width, int height, ImageObserver observer) {
        waitLoaded(originalImg);
        waitLoaded(watermark);

        int w = originalImg.getWidth(observer);
        int h = originalImg.getHeight(observer);

        if (0 > w && 0 > h) {
            waitLoaded(originalImg);
            w = originalImg.getWidth(observer);
            h = originalImg.getHeight(observer);
        }

        ImageIO.scanForPlugins();
        BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = img.createGraphics();
        g2d.drawImage(originalImg, 0, 0, w - 1, h, observer);
        g2d.drawImage(watermark, x, y, width, height, observer);
        g2d.dispose();

        return img;
    }

    public static BufferedImage read(File file) throws IOException {
        return ImageIO.read(file);
    }

    public static BufferedImage read(InputStream in) throws IOException {
        return ImageIO.read(in);
    }

    public static BufferedImage read(ImageInputStream iis) throws IOException {
        return ImageIO.read(iis);
    }

    public static boolean write(Image image, String format, File file) throws IOException {
        return ImageIO.write(toRendered(image), format, file);
    }

    public static boolean write(Image image, String format, OutputStream out) throws IOException {
        return ImageIO.write(toRendered(image), format, out);
    }

    public static boolean write(Image image, String format, ImageOutputStream out) throws IOException {
        return ImageIO.write(toRendered(image), format, out);
    }

    /**
     * 采用Toolkit.getDefaultToolkit().getImage 读取
     *
     * @param image
     * @return
     */
    public static Image load(File image, boolean waitLoaded) {
        Image img = Toolkit.getDefaultToolkit().getImage(image.getPath());
        if (waitLoaded) {
            waitLoaded(img);
        }
        return img;
    }

    public static RenderedImage toRendered(Image image) {
        return null == image || image instanceof RenderedImage ? (RenderedImage) image : toBuffered(image);
    }

    /**
     * 将 Image 对象转换为指定透明度的 BufferedImage
     *
     * @param image
     * @return
     */
    public static BufferedImage toBuffered(Image image) {
        if (image instanceof BufferedImage) {
            return (BufferedImage) image;
        }

        return clone(image);
    }

    public static BufferedImage clone(Image image) {
        int w = image.getWidth(null);
        int h = image.getWidth(null);

        // unloaded
        if (0 >= w && 0 >= h) {
            waitLoaded(image);

            w = image.getWidth(null);
            h = image.getHeight(null);
        }
        BufferedImage clone = new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
        Graphics2D g2d = clone.createGraphics();
        // 抗锯齿
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2d.drawImage(image, 0, 0, w, h, null);
        g2d.dispose();

        return clone;
    }

    private static String getFormat(Object input) throws IOException {
        try {
            ImageInputStream iis = ImageIO.createImageInputStream(input);
            Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
            iis.close();

            if (readers.hasNext()) {
                return readers.next().getFormatName();
            }
        } catch (Exception ignore) {
            // ignore
        }
        return null;
    }

    public static void waitLoaded(Image image) {
        MediaTracker tracker = getTracker();
        synchronized (tracker) {
            int id = getNextId();

            tracker.addImage(image, id);
            try {
                tracker.waitForID(id);
            } catch (InterruptedException e) {
                System.out.println("INTERRUPTED while loading Image");
            }
            tracker.removeImage(image, id);
        }
    }

    public static byte[] toBytes(Image img, String format) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        boolean write = ImageIO.write(toRendered(img), format, baos);
        return baos.toByteArray();
    }

    public static BufferedImage load(byte[] bytes) throws IOException {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        return ImageIO.read(bais);
    }

    private static final Object lock = new Object();
    private static MediaTracker tracker;
    private static int trackerId;

    private static int getNextId() {
        synchronized (getTracker()) {
            return ++trackerId;
        }
    }

    private static MediaTracker getTracker() {
        if (tracker != null) {
            return tracker;
        }
        synchronized (lock) {
            tracker = new MediaTracker(new Component() {
            });
        }
        return tracker;
    }
}
